Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node Search performance improvements #12056

Merged
merged 15 commits into from
Oct 4, 2021
Merged

Node Search performance improvements #12056

merged 15 commits into from
Oct 4, 2021

Conversation

pinzart90
Copy link
Contributor

@pinzart90 pinzart90 commented Sep 23, 2021

Simplify search to improve performance
Changes:

  1. Fix double search event caused by UI events
  2. Do not add the whole search text if there is only one word (using whitespace as separator)
  3. Limit search to 300 characters
  4. use OrdinalIgnoreCase option in IndexOf calls

Note:
Difference between culture sensitive and ordinal serarch:
https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#string-comparisons-that-use-the-current-culture

I would opt for OrdinalIgnoreCase because we get better performance...and the order of the returned results is not really a deal breaker (I think...)

@pinzart90 pinzart90 changed the title first pass Node Search performance improvements Sep 23, 2021
@pinzart90 pinzart90 added the PTAL Please Take A Look 👀 label Sep 23, 2021
@@ -105,8 +105,7 @@
Text="{Binding Path=SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
MinWidth="200"
CaretBrush="{StaticResource CommonSidebarTextColor}"
Margin="26,0,0,-1"
TextChanged="OnSearchTextBoxTextChanged" />
Margin="26,0,0,-1"/>
Copy link
Contributor Author

@pinzart90 pinzart90 Sep 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason the twoWay Binding to "SearchText" + TextChanged="OnSearchTextBoxTextChanged" causes 2 TextChanged events to be triggered for every input character

So I changed it so that we only bind the Text control to the "SearchText" Path.
When the text is changed, the "SearchText" property's setter will be called...and that is when we will execute the Search command

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this also be fixed by making this a one way binding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing around the binding types broke other parts of the UI (ex. making it one way did not update the Textbox initial value "Search" anymore)

@pinzart90 pinzart90 marked this pull request as ready for review September 23, 2021 18:50
@@ -13,6 +13,7 @@ namespace Dynamo.Search
public class SearchDictionary<V>
{
private ILogger logger;
private static int LIMIT_SEARCH_TAG_SIZE = 100;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be too small for some localized searches?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe...the value is definitely up for debate. I wonder if it would actually be best to warn the user if this limit is surpassed (I am not sure when or where)
I can take a look at the tags we got from Jostein to get a better idea of how many characters there are in a "normal" situation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Increased to 300

@@ -342,7 +343,7 @@ internal void RebuildTagDictionary()
tagAndWeight =>
new
{
Tag = tagAndWeight.Key,
Tag = tagAndWeight.Key.Substring(0, tagAndWeight.Key.Length > LIMIT_SEARCH_TAG_SIZE ? LIMIT_SEARCH_TAG_SIZE : tagAndWeight.Key.Length),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this actually useful at this point? Does the cost of all these extra conditional operations undo the potential savings?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same q here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well...after all search tags have been added/removed RebuildTagDictionary is called only once.
Performance penalty here should be negligible, compared to the constant searches on each text change.

subPatternsList.Insert(0, query);
subPatterns = (subPatternsList).ToArray();

if (subPatterns.Length > 1)// More than one word
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@mjkkirschner
Copy link
Member

@pinzart90 it looks good after the tests pass, can I ask you to manually check that regular library search still works?

@Amoursol
Copy link
Contributor

@pinzart do we have any metrics on how much better search is before and after this 🙏 ?

Comment on lines +386 to +388
var subPatternsList = subPatterns.ToList();
subPatternsList.Insert(0, query);
subPatterns = (subPatternsList).ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain with an example? If the query is all elements of category, the subpatterns list will be ["all", "elements", "of", "category"], then the subpatterns list will now be ["all elements of category", "all", "elements", "of", "category"]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is correct.
I think the logic behind it was to look for the full text first (a match with the full text should have the highest priority).

@pinzart90
Copy link
Contributor Author

@mjkkirschner I am working now on adding a performance test...and I will also check that existing functionality is not broken
@Amoursol I will get you those numbers

@pinzart90
Copy link
Contributor Author

Here are some numbers @Amoursol
These have been generated on my machine

With Fix "all elements of category" 5 times:
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 386ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 397ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 368ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 381ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 378ms -> 2

Without Fix "all elements of category" 5 times:
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 854ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 822ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 863ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 936ms -> 2
Searching for: "all elements of category", [Entries:1254, Tags:28110] : 869ms -> 2

With Fix "category" 5 times:
Searching for: "category", [Entries:1254, Tags:28110] : 78ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 80ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 71ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 66ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 71ms -> 20

Without Fix "category" 5 times:
Searching for: "category", [Entries:1254, Tags:28110] : 309ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 348ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 290ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 291ms -> 20
Searching for: "category", [Entries:1254, Tags:28110] : 282ms -> 20

Assert.AreEqual(results.Count(), 20);

int timeLimit = 260;//ms
Assert.IsTrue(Math.Abs(stopwatch.ElapsedMilliseconds - timeLimit) < 0.2 * timeLimit, $"Search time should be within a range of +/- 20% of {timeLimit}ms but we got {stopwatch.ElapsedMilliseconds}ms");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still might be a bit flaky ...

@@ -13,6 +13,7 @@ namespace Dynamo.Search
public class SearchDictionary<V>
{
private ILogger logger;
private static int LIMIT_SEARCH_TAG_SIZE = 300;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we all agree that 300 characters is good enough ?
Should we increase ?

Looking through the search tags we got from Jostein, I found the following:

  1. The longest search tag is 899 characters
  2. There are 34 search tags (out of 6525) that are over 300 characters

Copy link
Contributor Author

@pinzart90 pinzart90 Sep 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ALso this limit will be used in the Package Search window as well..(using package descriptions)
We could use another limit for Package Search ....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine by me but I'm still confused - these search tags from Jostein look like node descriptions. I'm curious if the entire description is used as a search tag, then what's the benefit of the search tags included in the <search></search> XML?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aparajit-pratap it's to inject extra search terms without adding them to the description.
So I can add box to the search terms for Cuboid - without needing to add box to the description.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pinzart90 what do you think about making this a hidden preference? I know it's more work, and kind of a pain, but it let's users tweak it if they run into trouble.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjkkirschner I added a new assembly config for it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, sorry @pinzart90 - I was not clear, by hidden preference -I just meant a preference in the dynamosettings.xml with no analog in the UI to control it. This is fine as well, users can still modify this if we want them to test something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjkkirschner if you think there is good enough reason to move to Preferences, then I can do that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a good idea to move it to the preference settings file so we have all preferences in one place. That is usually the one file where all such user preferences go.

@Amoursol
Copy link
Contributor

Here are some numbers @Amoursol
These have been generated on my machine...

Ok awesome, so that looks to be roughly a:

  • 56% performance boost on All Elements of Category
  • 76% performance boost on Category

That's pretty epic! We can discuss further works after 2.13 drops :) Thanks Tibi.

image

@@ -214,8 +214,6 @@ internal void Filter()
}
strBuilder.Append(", ");
}

Analytics.LogPiiInfo("Filter-categories", strBuilder.ToString().Trim());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this triggered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Analytics.LogPiiInfo is deprecated and actually does not log anything anymore. I just cleaned up the SearchViewModel class ...I do not think performance is affected by this though..

@@ -13,6 +13,7 @@ namespace Dynamo.Search
public class SearchDictionary<V>
{
private ILogger logger;
private static int LIMIT_SEARCH_TAG_SIZE = 300;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine by me but I'm still confused - these search tags from Jostein look like node descriptions. I'm curious if the entire description is used as a search tag, then what's the benefit of the search tags included in the <search></search> XML?

try
{
// Look up search tag limit in the assembly configuration
var assemblyConfig = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the DynamoCore.dll.config file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@pinzart90 pinzart90 merged commit f9c7691 into master Oct 4, 2021
@pinzart90 pinzart90 deleted the simplify_search branch October 4, 2021 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PTAL Please Take A Look 👀
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants